// pick default value if the param is undefined
function pickDefault(param, value)
{
	return (typeof param == 'undefined') ? value : param;
}

// build HTML for detailInfo of current node
// @param string json data of current node
function buildDetailInfo(data)
{
	// build attributes info for current node
	var attributes = '';
	$.each(data.attributes, function(name, value) {
		attributes += (attributes ? ", " : "<strong>Attributes:</strong> ") + name + "='" + value + "'";
	});
	attributes = (attributes != '') ? "<p>" + attributes + "</p>" : '';

	// build value info for current node
	var value = (data.childs.length == 0 && data.value != '') ? "<p><strong>Value:</strong> " + data.value + "</p>" : '';

	// build detail info for current node
	// detail info includes attributes and value
	var detailInfo = attributes + value;
	detailInfo = (detailInfo != '') ? ' detailInfo="<div style=\'padding: 0 10px 0 10px;\'>'+ detailInfo + '</div>"' : '';

	return detailInfo;
}

// build HTML for current node or child nodes
// @param string json data of current node
// @param bool [optional] if true building HTML include current node. Default is true.
// @param int [optional] number of level will be built HTML. Default is 1.
function buildTreeHTML(data, isDisplayCurrentNode, displayLevel)
{
	// set default for isDisplayCurrentNode
	isDisplayCurrentNode = pickDefault(isDisplayCurrentNode, true);
	isDisplayCurrentNode = isDisplayCurrentNode ? 1 : 0;
	// set default for displayLevel
	displayLevel = pickDefault(displayLevel, 1);
	if (!data.name || displayLevel < 1) {
		return '';
	}

	var content = '';

	// build html for all childs
	$.each(data.childs, function(i, child) {
		content += buildTreeHTML(child, true, displayLevel - isDisplayCurrentNode);
	});

	if (isDisplayCurrentNode) {
		var detailInfo = buildDetailInfo(data);

		content = '<li path="' + data.path + '">' + ((data.childs.length > 0) ? '<div class="hitarea"></div>' : '') + '<span class="nodeName"' + detailInfo + '>' + data.name + '</span>'
					+ ((data.childs.length > 0) ? '<ul>' + content + '</ul>' : '') + '</li>';
	}

	return content;
}

$.extend($.fn, {
	// swap between two classes
	swapClass: function(c1, c2) {
		var c1Elements = this.filter('.' + c1);
		this.filter('.' + c2).removeClass(c2).addClass(c1);
		c1Elements.removeClass(c1).addClass(c2);
		return this;
	},
	// replace first class by seconde class
	replaceClass: function(c1, c2) {
		return this.filter('.' + c1).removeClass(c1).addClass(c2).end();
	},
	// set class is className when hover
	hoverClass: function(className) {
		className = className || "hover";
		return this.hover(function() {
			$(this).toggleClass(className);
		});
	},
	// load tree HTML for a 'ul' control
	// @param string json data of current node
	// @param bool if true building HTML include current node.
	// @param int number of level will be loaded. Default is 1.
	loadTreeView: function(treeData, isDisplayCurrentNode, displayLevel) {
		var tree = $(this);

		// build html from json data
		var treeHTML = buildTreeHTML(treeData, isDisplayCurrentNode, displayLevel);

		// do nothing if the built html is empty
		if (treeHTML == ''){
			return;
		}

		// append the html to current <ul> tag
		tree.append(treeHTML);

		// set class for nodes have childs
		tree.find('>li:has(ul)').addClass('expandable');
		tree.find('>li:has(ul) >.hitarea').addClass('expandable-hitarea');

		// set class for last node 
		tree.find('>li:last-child:has(ul)').addClass('lastExpandable');
		tree.find('>li:last-child:has(ul)').find('>.hitarea').addClass('lastExpandable-hitarea');
		tree.find('>li:last-child:not(ul)').addClass('last');

		// set click event for nodes
		tree.find('>li:has(ul) >.hitarea, >li:has(ul) >.nodeName').click(function() {
			// thisNode: the node that happen click event
			var thisNode = $(this).parent();

			// clear all childs when collapse
			thisNode.find('>ul').html('');

			// swap classes for thisNode
			thisNode.swapClass('lastExpandable', 'lastCollapsable');
			thisNode.swapClass('expandable', 'collapsable');

			// swap classes for hitarea of thisNode
			thisNode.find('>.hitarea').swapClass('expandable-hitarea', 'collapsable-hitarea');
			thisNode.find('>.hitarea').swapClass('lastExpandable-hitarea', 'lastCollapsable-hitarea');

			// get path of the node
			var path = thisNode.attr('path');
			// call ajax to get data from server by it's path
			var data = $.ajax({
				url:"?ajax_call&path=" + path,
				async: false,
				success:function(result) {}
			});
			data = $.parseJSON(data.responseText);

			// reload all childs of thisNode (only 1 level)
			thisNode.find('>ul').loadTreeView(data, false, 1);
		});

		// set hover class for child nodes
		tree.find('>li >.hitarea, >li >.nodeName').hoverClass();

		// add tipsy for child nodes have detail infos
		tree.find('>li [detailInfo]').tipsy({fade: true, gravity: 'w', html: true, title: function() { return this.getAttribute('detailInfo') } });

		return this;
	}
});